﻿using Core.Structs;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Core.Tools
{
    public unsafe static class ArrayHelper
    {
        private delegate Array InternalCreateDelegate(void* ElementType, int Rank, int* Lengths, int* LowerBounds);
        private delegate void InternalGetReferenceDelegate(Array Instance, void* ElementRef, int Rank, int* Indices);
        private delegate void InternalSetValueDelegate(void* Target, object Value);

        private static readonly InternalCreateDelegate ArrayInternalCreate;
        private static readonly InternalGetReferenceDelegate ArrayInternalGetReference;
        private static readonly InternalSetValueDelegate ArrayInternalSetValue;

        private static readonly int[] Primes;
        private const int HashPrime = 101;

        /// <summary>
        /// 最大已定义字典容量自增值
        /// </summary>
        public const int MaxPrimeArrayLength = 0x7FEFFFFD;

        static ArrayHelper()
        {
            var ArrayInternalCreateMethod = typeof(Array).GetMethod("InternalCreate", BindingFlags.Static | BindingFlags.NonPublic);

            ArrayInternalCreate = (InternalCreateDelegate)Delegate.CreateDelegate(typeof(InternalCreateDelegate), ArrayInternalCreateMethod);



            var ArrayInternalGetReferenceMethod = typeof(Array).GetMethod("InternalGetReference", BindingFlags.Instance | BindingFlags.NonPublic);

            ArrayInternalGetReference = (InternalGetReferenceDelegate)Delegate.CreateDelegate(typeof(InternalGetReferenceDelegate), ArrayInternalGetReferenceMethod);



            var ArrayInternalSetValueMethod = typeof(Array).GetMethod("InternalSetValue", BindingFlags.Static | BindingFlags.NonPublic);

            ArrayInternalSetValue = (InternalSetValueDelegate)Delegate.CreateDelegate(typeof(InternalSetValueDelegate), ArrayInternalSetValueMethod);


            Primes = new int[] { 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239,
                293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049,
                4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023, 25229, 30293, 36353, 43627,
                52361, 62851, 75431, 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449,
                389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, 1674319, 2009191,
                2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 };
        }

        /// <summary>
        /// 创建一个数组对象
        /// </summary>
        /// <param name="ElementType">元素类型</param>
        /// <param name="Rank">维度</param>
        /// <param name="Lengths">数组长度</param>
        /// <returns>返回一个数组</returns>
        public static Array InternalCreate(Type ElementType, int Rank, int* Lengths)
        {
            return ArrayInternalCreate((void*)ElementType.TypeHandle.Value, Rank, Lengths, null);
        }

        /// <summary>
        /// 设置指定索引位置的元素值
        /// </summary>
        /// <param name="Array">数组</param>
        /// <param name="Rank">数组维度</param>
        /// <param name="Indices">索引</param>
        /// <param name="Value">值</param>
        public static void SetValue(Array Array, int Rank, int* Indices, object Value)
        {
            TypedReference ElementRef;

            ArrayInternalGetReference(Array, &ElementRef, Rank, Indices);

            ArrayInternalSetValue(&ElementRef, Value);
        }
        
        /// <summary>
        /// 判断一个自增值是否有效
        /// </summary>
        /// <param name="candidate">自增值</param>
        /// <returns>返回一个 bool 值。</returns>
        private static bool IsPrime(int candidate)
        {
            if ((candidate & 1) != 0)
            {
                int limit = (int)Math.Sqrt(candidate);
                for (int divisor = 3; divisor <= limit; divisor += 2)
                {
                    if ((candidate % divisor) == 0)
                        return false;
                }
                return true;
            }
            return (candidate == 2);
        }

        /// <summary>
        /// 获得已定义的字典自增值
        /// </summary>
        /// <param name="min">最小值</param>
        /// <returns>返回一个 int 值。</returns>
        public static int GetPrime(int min)
        {
            for (int i = 0; i < Primes.Length; i++)
            {
                int prime = Primes[i];
                if (prime >= min) return prime;
            }

            for (int i = (min | 1); i < int.MaxValue; i += 2)
            {
                if (IsPrime(i) && ((i - 1) % HashPrime != 0))
                    return i;
            }

            return min;
        }

        /// <summary>
        /// 遍历 NameValueCollection 集合
        /// </summary>
        /// <param name="Collection">需要遍历的集合</param>
        /// <param name="Function">回调函数，当此回调函数返回 true 时则继续遍历，返回 false 则停止。</param>
        public unsafe static void Each(NameValueCollection Collection, Func<string, string, bool> Function)
        {
            if (Collection == null)
            {
                return;
            }

            var LpColl = ((NameValueCollectionStruct*)Pointer.UnBox(Collection));

            if (LpColl->_entriesArray == null)
            {
                return;
            }

            ArrayList ArrayContext = (ArrayList)Pointer.Box(LpColl->_entriesArray);

            foreach (var Item in ArrayContext)
            {
                if (Item != null)
                {
                    var LpItem = (NameValueCollectionEntryStruct*)Pointer.UnBox(Item);

                    if (LpItem->Key == null)
                    {
                        continue;
                    }

                    var Key = (string)Pointer.Box(LpItem->Key);
                    var Value = (ArrayList)Pointer.Box(LpItem->Value);

                    foreach (string ValueItem in Value)
                    {
                        Function(Key, ValueItem);
                    }
                }
            }
        }

        /// <summary>
        /// 筛选数组元素
        /// </summary>
        /// <typeparam name="TIn">输入数组类型</typeparam>
        /// <typeparam name="TOut">输出数组类型</typeparam>
        /// <param name="InArray">输入数组</param>
        /// <param name="Filter">输入数组筛选器</param>
        /// <param name="AsValue">输入数组元素转输出数组元素委托</param>
        /// <returns>返回一个新的数组</returns>
        public static TOut[] Filter<TIn, TOut>(TIn[] InArray, Func<TIn, bool> Filter, Func<TIn, TOut> AsValue)
        {
            TOut[] NewArray = new TOut[InArray.Length];

            int ResultLength = 0;

            foreach (var Item in InArray)
            {
                if (Filter(Item))
                {
                    NewArray[ResultLength] = AsValue(Item);

                    ++ResultLength;
                }
            }

            if (ResultLength != InArray.Length)
            {
                Array.Resize(ref NewArray, ResultLength);
            }

            return NewArray;
        }

        /// <summary>
        /// 筛选数组元素
        /// </summary>
        /// <typeparam name="TIn">输入数组类型</typeparam>
        /// <typeparam name="TOut">输出数组类型</typeparam>
        /// <param name="InSource">输入源</param>
        /// <param name="Filter">输入数组筛选器</param>
        /// <param name="AsValue">输入数组元素转输出数组元素委托</param>
        /// <returns>返回一个新的数组</returns>
        public static TOut[] Filter<TIn, TOut>(IEnumerable<TIn> InSource, Func<TIn, bool> Filter, Func<TIn, TOut> AsValue)
        {
            TOut[] NewArray = new TOut[InSource.Count()];

            int ResultLength = 0;

            foreach (var Item in InSource)
            {
                if (Filter(Item))
                {
                    NewArray[ResultLength] = AsValue(Item);

                    ++ResultLength;
                }
            }

            if (ResultLength != NewArray.Length)
            {
                Array.Resize(ref NewArray, ResultLength);
            }

            return NewArray;
        }

        /// <summary>
        /// 将字符串以逗号分隔然后填充到数组中
        /// </summary>
        /// <param name="Text">字符串</param>
        /// <param name="Array">数组</param>
        /// <param name="Begin">数组开始索引</param>
        public static void ParseArray(string Text, Array Array, int Begin)
        {
            fixed (char* LpText = Text)
            {
                ParseArray(LpText, 0, Text.Length, Array, Begin);
            }
        }

        public static Array ParseArray(string Text)
        {
            fixed (char* LpText = Text)
            {
                return ParseArray(LpText, 0, Text.Length);
            }
        }

        internal static Array ParseArray(char* LpText, int Begin, int End)
        {
            if (Begin >= End)
            {
                return new string[] { "" };
            }

            int BeginBack = Begin;

            int Length = 0;

            Loop:
            int Index = StringHelper.IndexOf(LpText, ',', '\\', Begin, End);

            if (Index == -1)
            {
                Index = End;
            }
            else if (LpText[Index] == '\\')
            {
                do
                {
                    Index = StringHelper.IndexOf(LpText, ',', '\\', Index + 2, End);

                } while (Index != -1 && LpText[Index] != '\\');

                if (Index == -1)
                {
                    Index = End;
                }
            }

            ++Length;

            Begin = Index + 1;

            if (Index != End)
            {
                goto Loop;
            }

            object[] Result = new object[Length];

            ParseArray(LpText, BeginBack, End, Result, 0);

            return Result;
        }

        internal static void ParseArray(char* LpText, int Begin, int End, Array Array, int ArrayIndex)
        {
            if (Array == null)
            {
                throw new NullReferenceException("Array");
            }

            if (Array.Rank != 1)
            {
                throw new RankException("Array.Rank != 1");
            }

            if (Begin >= End)
            {
                return;
            }

            var ElementType = Array.GetType().GetElementType();

            Loop:
            int Index = StringHelper.IndexOf(LpText, ',', '\\', Begin, End);

            if (Index == -1)
            {
                Index = End;
            }
            else if (LpText[Index] == '\\')
            {
                do
                {
                    Index = StringHelper.IndexOf(LpText, ',', '\\', Index + 2, End);

                } while (Index != -1 && LpText[Index] != '\\');

                if (Index == -1)
                {
                    Index = End;
                }
            }

            object Value = ParseValue(UnEscape(LpText, Begin, Index - 1));

            if (!(ElementType.IsInstanceOfType(Value)))
            {
                Value = Convert.ChangeType(Value, ElementType);
            }

            SetValue(Array, 1, &ArrayIndex, Value);

            ++ArrayIndex;

            Begin = Index + 1;

            if (Index != End)
            {
                goto Loop;
            }
        }

        internal static string UnEscape(char* LpText, int Begin, int End)
        {
            if (End < Begin)
            {
                return "";
            }

            int EscapeCount = 0;

            for (int i = Begin; i < End; ++i)
            {
                if (LpText[i] == '\\')
                {
                    ++EscapeCount;

                    ++i;
                }
            }

            if (EscapeCount == 0)
            {
                return StringHelper.Substring(LpText, Begin, End + 1);
            }

            int Length = End - Begin - EscapeCount + 1;

            string Result = new string('\0', Length);

            fixed (char* LpResult = Result)
            {
                for (int i = 0; Begin <= End; ++i, ++Begin)
                {
                    if (LpText[Begin] == '\\')
                    {
                        ++Begin;

                        LpResult[i] = LpText[Begin];
                    }
                    else
                    {
                        LpResult[i] = LpText[Begin];
                    }
                }
            }

            return Result;
        }

        internal static object ParseValue(string Text)
        {
            switch (Text)
            {
                case "null":
                    return null;
                case "true":
                    return true;
                case "false":
                    return false;
            }

            if (Text.Length == 0)
            {
                return "";
            }

            bool IsNegative;
            bool IsFloat;
            int Digit;
            int Float;

            if (NumberHelper.IsNumber(Text, 10, out IsNegative, out IsFloat, out Digit, out Float))
            {
                if (IsFloat)
                {
                    if (Digit + Float <= 15)
                    {
                        return double.Parse(Text);
                    }
                    else if (Digit + Float <= 28)
                    {
                        return decimal.Parse(Text);
                    }
                }
                else if (Digit <= 9)
                {
                    return int.Parse(Text);
                }
                else if (Digit <= 18)
                {
                    return long.Parse(Text);
                }
                else if (Digit <= 28)
                {
                    return decimal.Parse(Text);
                }

                return double.Parse(Text);
            }

            return Text;
        }
    }
}